package top.yvyan.guettable.bean;

import static java.lang.Double.parseDouble;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import top.yvyan.guettable.Gson.ExamScoreNew;
import top.yvyan.guettable.Gson.ProgramModel;
import top.yvyan.guettable.Gson.Semester;
import top.yvyan.guettable.data.MoreData;

public class GradesBean {


    public List<Score> scoreList = new ArrayList<>();

    public Map<String, List<Score>> courseMap = new HashMap<>();

    public Map<String, List<Score>> yearMap = new HashMap<>();

    public List<String> years = new ArrayList<>(List.of("所有"));

    public Map<String, Semester> semesterMap;
    public List<String> semesterIdList;

    public Map<String, Boolean> creditCourse = new HashMap<>();

    public UserOverride userOverride = new UserOverride();

    public static class UserOverride {
        public Map<String, Boolean> creditCourse; // 覆盖学分计入状态
        public Map<String, Boolean> preferClass;

        public UserOverride() {
            this.creditCourse = new HashMap<>();
            this.preferClass = new HashMap<>();
        }
    }

    public static class Score implements Cloneable {
        public Double credits; // 学分
        public String courseCode;
        public String courseName; // 课程名称
        public String courseProperty;

        public String semesterName; // 学期
        public Semester semester; // 学期
        public double score;
        public String gaGrade;

        public Integer id;

        public boolean consider; // 是否计入学分绩

        @NonNull
        @Override
        public Score clone() {
            try {
                return (Score) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    }

    public double parseGaGrade(String gaGrade) {
        switch (gaGrade) {
            case "优":
                return 95;
            case "良":
                return 85;
            case "中":
                return 75;
            case "合格":
            case "及格":
                return 65;
            case "不合格":
            case "不及格":
                return 40;
            default:
                try {
                    return parseDouble(gaGrade);
                } catch (Exception ignored) {

                }
        }
        return 100;
    }

    public GradesBean(ExamScoreNew examscorenew, ProgramModel model, Map<String, Semester> semesterMap, UserOverride userOverride) {
        if (userOverride != null) {
            this.userOverride = userOverride;
        }
        this.semesterMap = semesterMap;
        this.semesterIdList = new ArrayList<>(examscorenew.semesterId2studentGrades.keySet());
        for (Map.Entry<String, List<ExamScoreNew.ScoreDetail>> scores : examscorenew.semesterId2studentGrades.entrySet()) {
            for (ExamScoreNew.ScoreDetail score : scores.getValue()) {
                Score gradeScore = new Score();
                gradeScore.id = score.id;
                gradeScore.credits = score.credits;
                gradeScore.courseCode = score.courseCode;
                gradeScore.courseName = score.courseName;
                gradeScore.courseProperty = score.courseProperty;
                gradeScore.semesterName = score.semesterName;
                gradeScore.semester = semesterMap.get(scores.getKey());
                gradeScore.score = parseGaGrade(score.gaGrade);
                gradeScore.gaGrade = score.gaGrade;
                gradeScore.consider = false;
                this.scoreList.add(gradeScore);
            }
        }
    }

    public void initRule() {
        creditCourse = new HashMap<>();
        for (Score score : this.scoreList) {
            if (creditCourse.get(score.courseCode) == null) {
                creditCourse.put(score.courseCode, !score.courseProperty.equals("通识教育") && !score.courseProperty.equals("专业任选"));
            }
        }
        if (this.userOverride == null || this.userOverride.preferClass == null || this.userOverride.creditCourse == null) {
            this.userOverride = new UserOverride();
        }
        creditCourse.putAll(userOverride.creditCourse);
    }

    public void initStruct() {
        this.initRule();
        courseMap = new HashMap<>();
        yearMap = new HashMap<>();
        years = new ArrayList<>(List.of("所有"));


        if (semesterIdList == null || semesterIdList.isEmpty()) { // 兼容旧版本
            semesterIdList = new ArrayList<>(semesterMap.keySet());
        }

        // 按时间序排序学期列表
        Collections.sort(semesterIdList, (a, b) -> {
            Semester semesterA = semesterMap.get(a);
            Semester semesterB = semesterMap.get(b);
            Long startTimeA = Long.MAX_VALUE;
            Long startTimeB = Long.MAX_VALUE;
            if (semesterA != null) {
                startTimeA = semesterA.getStartDateTime();
            }
            if (semesterB != null) {
                startTimeB = semesterB.getStartDateTime();
            }
            return startTimeA.equals(startTimeB) ? 0 : (startTimeA > startTimeB ? 1 : -1);
        });

        for (String semesterId : semesterIdList) {
            Semester semester = semesterMap.get(semesterId);
            if (semester != null) {
                if (!years.contains(semester.schoolYear)) {
                    years.add(semester.schoolYear);
                }
                yearMap.put(semester.schoolYear, new ArrayList<>());
            }
        }

        // 按学期归类成绩单
        List<Score> globalList = new ArrayList<>();
        for (Score score : scoreList) {
            List<Score> sameschoolYearList = yearMap.get(score.semester.schoolYear);
            if (sameschoolYearList != null) {
                sameschoolYearList.add(score.clone());
            } else {
                sameschoolYearList = new ArrayList<>();
                sameschoolYearList.add(score.clone());
                yearMap.put(score.semester.schoolYear, sameschoolYearList);
            }
            globalList.add(score);
        }
        yearMap.put("所有", globalList);
    }

    public void calcGrades() {
        // 按学期遍历成绩单，选择计入学分绩的课程 (1.用户倾向课程 2.学期内同课程代码的最高分课程)
        for (Map.Entry<String, List<Score>> yearScores : yearMap.entrySet()) {
            Map<String, List<Score>> courseMap = new HashMap<>();
            List<Score> Scores = yearScores.getValue();
            for (Score score : yearScores.getValue()) {

                score.consider = false;// 重置计入状态为 false

                List<Score> sameCourseCodeList;
                // 按课程代码分类
                if ((sameCourseCodeList = courseMap.get(score.courseCode)) != null) {
                    sameCourseCodeList.add(score);
                } else {
                    sameCourseCodeList = new ArrayList<>();
                    sameCourseCodeList.add(score);
                    courseMap.put(score.courseCode, sameCourseCodeList);
                }
            }

            // 存储全局课程映射
            if (yearScores.getKey().equals("所有")) {
                this.courseMap = courseMap;
            }

            Scores.clear(); // 清空成绩项，重新应用排序后的列表

            // 排序规则查询 Map
            Map<String, Long> courseTimeMap = new HashMap<>(); // 课程时间
            Map<String, Double> courseScoreMap = new HashMap<>(); // 课程成绩

            // 选择计入学分绩的成绩项，并排序信息
            for (Map.Entry<String, List<Score>> coursesEntry : courseMap.entrySet()) {
                List<Score> scores = coursesEntry.getValue();
                String courseCode = coursesEntry.getKey();
                // 课程内部按成绩排序
                Collections.sort(scores, (a, b) -> (int) (b.score - a.score));

                Boolean consider = creditCourse.get(courseCode); // 该课程是否计入学分绩

                Long courseTime = Long.MAX_VALUE;
                double courseScore = 0.0;
                // 选择计入学分绩的成绩项
                if (consider != null && consider) {
                    boolean flag = false;
                    for (Score score : scores) {
                        if (score.id != null) {
                            Boolean prefer = userOverride.preferClass.get(score.id.toString());
                            if (prefer != null && prefer) {
                                score.consider = true;
                                flag = true;
                                break;
                            }
                        }
                    }
                    if (!flag && !scores.isEmpty()) {
                        scores.get(0).consider = true;
                    }
                }

                for (Score score : scores) {
                    // 获取课程时间，以最晚的成绩周期为准
                    if (score.semester != null) {
                        Long scoreTime = score.semester.getStartDateTime();
                        if (courseTime == Long.MAX_VALUE) {
                            courseTime = scoreTime;
                        } else if (scoreTime > courseTime) {
                            courseTime = scoreTime;
                        }
                    }
                    if (score.score > courseScore) {
                        courseScore = score.score;
                    }
                }
                courseTimeMap.put(courseCode, courseTime);
                courseScoreMap.put(courseCode, courseScore);
            }

            List<String> courseList = new ArrayList<>(courseMap.keySet()); // 获取课程列表

            // 应用排序规则，课程项之间按照 1.时间 2.最高成绩 的优先级进行排序
            Collections.sort(courseList, (a, b) -> {
                Long courseATime = courseTimeMap.get(a);
                Long courseBTime = courseTimeMap.get(b);
                if (courseATime == null) {
                    courseATime = Long.MAX_VALUE;
                }
                if (courseBTime == null) {
                    courseBTime = Long.MAX_VALUE;
                }

                if (!courseATime.equals(courseBTime)) {
                    return courseATime > courseBTime ? 1 : -1;
                }

                Double courseAScore = courseScoreMap.get(a);
                Double courseBScore = courseScoreMap.get(b);

                if (courseAScore == null) {
                    courseAScore = 0.0;
                }
                if (courseBScore == null) {
                    courseBScore = 0.0;
                }

                if (courseAScore.equals(courseBScore)) {
                    return 0;
                }

                return courseBScore > courseAScore ? 1 : -1;

            });

            for (String courseCode : courseList) {
                // 按照排序规则重新应用列表，课程内部已按成绩排序
                List<Score> scores = courseMap.get(courseCode);
                if (scores != null) {
                    Scores.addAll(scores);
                }
            }

        }
    }


    /**
     * 切换用户自定义规则
     *
     * @param yearIndex  学年
     * @param scoreIndex 成绩索引
     */
    public void switchCustomRule(int yearIndex, int scoreIndex) {
        switchCustomRuleInternal(yearIndex, scoreIndex);
        initRule();
        calcGrades();
        MoreData.setGradesBean(this);
    }

    private void switchCustomRuleInternal(int yearIndex, int scoreIndex) {
        if (userOverride == null || userOverride.creditCourse == null || userOverride.preferClass == null) {
            return;
        }
        if (yearIndex >= years.size()) return;
        String year = years.get(yearIndex);
        List<Score> yearScores = yearMap.get(year);
        if (yearScores == null) return;
        if (scoreIndex >= yearScores.size()) return;
        Score score = yearScores.get(scoreIndex);
        if (score == null) return;
        if (score.id != null && score.courseCode != null) {
            if (score.consider) { // 若成绩项计入，切换为不计入状态
                List<Score> sameCourseScores = courseMap.get(score.courseCode);
                if (sameCourseScores == null) return;
                // 有 prefer 标记则移除 prefer 标记
                userOverride.preferClass.remove(score.id.toString());
                Boolean creditCourse = userOverride.creditCourse.get(score.courseCode);
                if (creditCourse != null) { // 有 creditCourse 标记则清除 creditCourse 标记
                    userOverride.creditCourse.remove(score.courseCode);
                } else {
                    // 没有 creditCourse 标记则设置 creditCourse = false
                    userOverride.creditCourse.put(score.courseCode, false);
                }
            } else { // 若成绩项不计入，切换为计入状态
                Boolean creditCourse = this.creditCourse.get(score.courseCode);
                if (creditCourse != null && !creditCourse) {
                    // 若课程不计入，添加用户计入规则
                    Boolean userCreditCourse = userOverride.creditCourse.get(score.courseCode);
                    if (userCreditCourse != null) { // 有 creditCourse 标记则清除 creditCourse 标记
                        userOverride.creditCourse.remove(score.courseCode);
                    } else {
                        // 没有 creditCourse 标记则设置 creditCourse = true
                        userOverride.creditCourse.put(score.courseCode, true);
                    }
                }
                List<Score> sameCourseScores = courseMap.get(score.courseCode);
                if (sameCourseScores == null) return;
                for (Score sameCourseScore : sameCourseScores) {
                    // 清除所有 prefer 标记
                    if (sameCourseScore.id == null) continue;
                    userOverride.preferClass.remove(sameCourseScore.id.toString());
                }
                userOverride.preferClass.put(score.id.toString(), true); // 设置 prefer = true
            }
        }
    }

    public static double calcGrade(List<Score> scores) {
        double creditSum = 0;
        double scoreSum = 0;
        for (Score score : scores) {
            if (!score.consider) continue;
            creditSum += score.credits;
            scoreSum += score.score * score.credits;
        }
        return scoreSum / creditSum;
    }

    public static double calcCreditSum(List<Score> scores) {
        double creditSum = 0;
        for (Score score : scores) {
            if (!score.consider) continue;
            creditSum += score.credits;
        }
        return creditSum;
    }

}
